<?php

class CurlHttp {
	/* 并发请求设置项 */
	private $limitCount = 10; //并发请求数量
	public $returninfoswitch = false;  //是否返回请求信息，开启后单项请求返回结果为:
	                                    //array('info'=>请求信息, 'result'=>返回内容, 'error'=>错误信息)

	//私有属性
	private $singlequeue = null;
	private $rollqueue = null;
	private $_requstItems = null;
	private $_callback = null;
	private $_result;
	private $_referer = null;
	private $_cookies = array();
	private $_resheader;
	private $_reqheader = array();
	private $_resurl;
	private $_redirect_url;
	private $referer;

	private $_singleoptions = array(
			CURLOPT_RETURNTRANSFER => true,         // return web page
			CURLOPT_HEADER         => true,        // don't return headers
			// 			CURLOPT_FOLLOWLOCATION => true,         // follow redirects
			CURLOPT_NOSIGNAL      =>true,
			CURLOPT_ENCODING       => "",           // handle all encodings
			CURLOPT_USERAGENT      => "",           // who am i
			CURLOPT_AUTOREFERER    => true,         // set referer on redirect
			CURLOPT_CONNECTTIMEOUT => 120,          // timeout on connect
			CURLOPT_TIMEOUT        => 120,          // timeout on response
			CURLOPT_MAXREDIRS      => 10,           // stop after 10 redirects
			CURLOPT_SSL_VERIFYHOST => 0,            // don't verify ssl
			CURLOPT_SSL_VERIFYPEER => false,        //
	);
	
	private $_rolloptions = array(
			CURLOPT_RETURNTRANSFER => true,         // return web page
			CURLOPT_HEADER         => false,        // don't return headers
			// 			CURLOPT_FOLLOWLOCATION => true,         // follow redirects
			CURLOPT_NOSIGNAL      =>true,
			CURLOPT_ENCODING       => "",           // handle all encodings
			CURLOPT_USERAGENT      => "",           // who am i
			CURLOPT_AUTOREFERER    => true,         // set referer on redirect
			CURLOPT_CONNECTTIMEOUT => 120,          // timeout on connect
			CURLOPT_TIMEOUT        => 120,          // timeout on response
			CURLOPT_MAXREDIRS      => 10,           // stop after 10 redirects
			CURLOPT_SSL_VERIFYHOST => 0,            // don't verify ssl
			CURLOPT_SSL_VERIFYPEER => false,        //
	);

	function singleInit($options = array()) {
		if (!$this->singlequeue) {
			$this->singlequeue = curl_init();
		}
		if ($options) {
			$this->_singleoptions = array_merge($this->_singleoptions, $options);
		}
	}
	function rollInit($options = array()) {
		if(!$this->rollqueue){
			$this->rollqueue = curl_multi_init();
		}
		if ($options) {
			$this->_rolloptions = array_merge($this->_rolloptions, $options);
		}
	}

	/**
	 * @name 返回Header数组
	 * @param resource $ch
	 * @param $result
	 * @return string
	 */
	private function getResRawHeader($ch, $result) {
		$ch_info = curl_getinfo($ch);
		$header_size = $ch_info["header_size"];
		$rawheader = substr($result, 0, $ch_info['header_size']);
		return $rawheader;
	}

	/**
	 * @name 返回Header数组
	 * @param resource $ch
	 * @param $result
	 * @return string
	 */
	private function getResHeader($ch, $result) {
		$header = array();
		$rawheader = $this->getResRawHeader($ch, $result);
		if(preg_match_all('/([^:\s]+): (.*)/i', $rawheader, $header_match)){
			for($i=0;$i<count($header_match[0]);$i++){
				$header[$header_match[1][$i]] = $header_match[2][$i];
			}
		}
		return $header;
	}

	private function getResBody($ch, $result) {
		$ch_info = curl_getinfo($ch);
		$body = substr($result, -$ch_info['download_content_length']);
		return $body;
	}

	private function getResCookies($ch, $result) {
		$rawheader = $this->getResRawHeader($ch, $result);
		$cookies = array();
		if(preg_match_all('/Set-Cookie:(?:\s*)([^=]*?)=([^\;]*?);/i', $rawheader, $cookie_match)){
			for($i=0;$i<count($cookie_match[0]);$i++){
				$cookies[$cookie_match[1][$i]] = $cookie_match[2][$i];
			}
		}
		return $cookies;
	}

	private function setReqCookies($ch, $reqcookies = array()) {
		$reqCookiesString = "";
		if(!empty($reqcookies)){
			if(is_array($reqcookies)){
				foreach ($reqcookies as $key => $val){
					$reqCookiesString .=  $key."=".$val."; ";
				}
				curl_setopt($ch, CURLOPT_COOKIE, $reqCookiesString);
			}
		}elseif(!empty($this->_cookies)) {
			foreach ($this->_cookies as $key => $val){
				$reqCookiesString .=  $key."=".$val."; ";
			}
			curl_setopt($ch, CURLOPT_COOKIE, $reqCookiesString);
		}
	}
	private function setResCookies($ch) {
		if(!empty($reqcookies)&&is_array($reqcookies)){
			$this->_cookies = array_merge($this->_cookies, $reqcookies);
		}
	}

	function post($url, $postfields=null, $referer=null, $reqcookies=null, $reqheader=array())
	{
		$this->singlequeue = curl_init($url);
		$options = array(
				CURLOPT_RETURNTRANSFER => true,         // return web page
				CURLOPT_HEADER         => true,        // don't return headers
				// 				CURLOPT_FOLLOWLOCATION => true,         // follow redirects
				CURLOPT_ENCODING       => "",           // handle all encodings
				CURLOPT_USERAGENT      => "",     // who am i
				CURLOPT_AUTOREFERER    => true,         // set referer on redirect
				CURLOPT_CONNECTTIMEOUT => 120,          // timeout on connect
				CURLOPT_TIMEOUT        => 120,          // timeout on response
				CURLOPT_MAXREDIRS      => 10,           // stop after 10 redirects
				CURLOPT_POST            => true,            // i am sending post data
				CURLOPT_SSL_VERIFYHOST => 0,            // don't verify ssl
				CURLOPT_SSL_VERIFYPEER => false,        //
		);
		curl_setopt_array($this->singlequeue, $options);
		curl_setopt($this->singlequeue, CURLOPT_POSTFIELDS, $postfields);   // this are my post vars
		if($referer){
			curl_setopt($this->singlequeue, CURLOPT_REFERER, $referer);
		}
		elseif ($this->referer){
			curl_setopt($this->singlequeue, CURLOPT_REFERER, $this->referer);
		}

		$this->setReqheader($this->singlequeue, $reqheader);
		$this->setReqCookies($this->singlequeue, $reqcookies);

		$result = curl_exec($this->singlequeue);
		$resCookies = $this->getResCookies($this->singlequeue, $result);;
		if (is_array($resCookies)&&!empty($resCookies)) {
			$this->_cookies = array_merge($this->_cookies ,$resCookies);
		}
		$resHeader = $this->getResHeader($this->singlequeue, $result);
		if (is_array($resHeader)&&!empty($resHeader)) {
			$this->_resheader = $resHeader;
		}
		$this->_result = $this->getResBody($this->singlequeue, $result);
		curl_close($this->singlequeue);
		$this->singlequeue = null;
		return $this->_result;

	}

	function get($url, $referer=null, $reqcookies=null, $reqheader=array())
	{
		msg("access URL " . $url);
		$this->singlequeue = curl_init($url);
		$options = array(
				CURLOPT_RETURNTRANSFER => true,         // return web page
				CURLOPT_HEADER         => true,        // don't return headers
				// 				CURLOPT_FOLLOWLOCATION => true,         // follow redirects
				CURLOPT_ENCODING       => "",           // handle all encodings
				CURLOPT_USERAGENT      => "",     // who am i
				CURLOPT_AUTOREFERER    => true,         // set referer on redirect
				CURLOPT_CONNECTTIMEOUT => 120,          // timeout on connect
				CURLOPT_TIMEOUT        => 120,          // timeout on response
				CURLOPT_MAXREDIRS      => 10,           // stop after 10 redirects
				CURLOPT_POST            => false,            // i am sending post data
				CURLOPT_SSL_VERIFYHOST => 0,            // don't verify ssl
				CURLOPT_SSL_VERIFYPEER => false,        //
				CURLOPT_REFERER        =>$referer,
		);
		curl_setopt_array($this->singlequeue, $options);
		if($referer){
			curl_setopt($this->singlequeue, CURLOPT_REFERER, $referer);
		}
		elseif ($this->referer){
			curl_setopt($this->singlequeue, CURLOPT_REFERER, $this->referer);
		}
		$this->setReqheader($this->singlequeue, $reqheader);
		$this->setReqCookies($this->singlequeue, $reqcookies);

		$result = curl_exec($this->singlequeue);
		$resCookies = $this->getResCookies($this->singlequeue, $result);
		if (is_array($resCookies)&&!empty($resCookies)) {
			$this->_cookies = array_merge($this->_cookies ,$resCookies);
		}
		$resHeader = $this->getResHeader($this->singlequeue, $result);
		if (is_array($resHeader)) {
			$this->_resheader = $resHeader;
		}
		$this->_result = $this->getResBody($this->singlequeue, $result);
		curl_close($this->singlequeue);
		$this->singlequeue = null;
		return $this->_result;
	}

	function rollRequest($requestArray, $callback="")
	{
		$this->_requstItems = $requestArray;
		$requestArrayKeys = array_keys($requestArray);
		/* 		$requestArray = array(
		 array(
		 		'url' => "",
		 		'method' => "post",
		 		'postfields' => array(),
		 		'cookies' => "",
		 		'referer' => "",
		 ),
				array(
						'url' => "",
						'postfields' => array(),
						'cookies' => "",
						'referer' => "",
				),
		); */
		$this->rollqueue = curl_multi_init();
		$map = array();
		for ($i=0;$i<$this->limitCount && !empty($requestArrayKeys);$i++)
		{
			$keyvalue = array_shift($requestArrayKeys);
			$this->addToRollQueue( $requestArray, $keyvalue, $map );

		}

		$responses = array();
		do {
			while (($code = curl_multi_exec($this->rollqueue, $active)) == CURLM_CALL_MULTI_PERFORM) ;

			if ($code != CURLM_OK) {
				break;
			}

			// 找到刚刚完成的任务句柄
			while ($done = curl_multi_info_read($this->rollqueue)) {
				// 处理当前句柄的信息、错误、和返回内容
				$info = curl_getinfo($done['handle']);
				$error = curl_error($done['handle']);
				if ($this->_callback)
				{
					//调用callback函数处理当前句柄的返回内容，callback函数参数有：（返回内容, 队列id）
					$result = call_user_func($this->_callback, curl_multi_getcontent($done['handle']), $map[(string) $done['handle']]);
				}
				else
				{
					//如果callback为空，直接返回内容
					$result = curl_multi_getcontent($done['handle']);
				}
				if ($this->returninfoswitch) {
					$responses[$map[(string) $done['handle']]] = compact('info', 'error', 'result');
				}
				else
				{
					$responses[$map[(string) $done['handle']]] = $result;
				}

				// 从队列里移除上面完成处理的句柄
				curl_multi_remove_handle($this->rollqueue, $done['handle']);
				curl_close($done['handle']);
				if (!empty($requestArrayKeys))
				{
					$addkey = array_shift($requestArrayKeys);
					$this->addToRollQueue ( $requestArray, $addkey, $map );
				}
			}

			// Block for data in / output; error handling is done by curl_multi_exec
			if ($active > 0) {
				curl_multi_select($this->rollqueue, 0.5);
			}

		} while ($active);

		curl_multi_close($this->rollqueue);
		$this->rollqueue = null;
		return $responses;
	}

	private function addToRollQueue($requestArray, $keyvalue, &$map) {
		$ch = curl_init();
		curl_setopt_array($ch, $this->_rolloptions);
		//检查提交方式，并设置对应的设置，为空的话默认采用get方式
		if ("post" === $requestArray[$keyvalue]['method'])
		{
			curl_setopt($ch, CURLOPT_POST, true);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $requestArray[$keyvalue]['postfields']);
		}
		else
		{
			curl_setopt($ch, CURLOPT_HTTPGET, true);
		}


		if($requestArray[$keyvalue]['referer']){
			curl_setopt($ch, CURLOPT_REFERER, $requestArray[$keyvalue]['referer']);
		}
		elseif ($this->referer){
			curl_setopt($ch, CURLOPT_REFERER, $this->referer);
		}
		$this->setReqheader($ch, $requestArray[$keyvalue]['header']);
		//cookies设置
		$this->setReqCookies($ch, $requestArray[$keyvalue]['cookies']);

		curl_setopt($ch, CURLOPT_URL, $requestArray[$keyvalue]['url']);
		curl_setopt($ch, CURLOPT_REFERER, $requestArray[$keyvalue]['referer']);
		curl_multi_add_handle($this->rollqueue, $ch);
		$map[(string) $ch] = $keyvalue;
	}

	public function getRollLimitCount() {
		return $this->limitCount;
	}

	public function setRollLimitCount($limitCount) {
		$this->limitCount = $limitCount;
		return $this;
	}

	public function setCallback($_callback) {
		$this->_callback = $_callback;
		return $this;
	}

	public function getResult() {
		return $this->_result;
	}

	public function getRawHeader() {
		return $this->_resheader;
	}

	public function getCookies() {
		return $this->_cookies;
	}

	public function setCookies($_cookies) {
		$this->_cookies = $_cookies;
		return $this;
	}

	public function setHeader($header) {
		$this->_reqheader = array_merge($this->_reqheader, $header);
		return $this;
	}

	private function setReqheader($ch, $reqheader) {
		$reqheader = array_merge($this->_reqheader, $reqheader);
		if (is_array($reqheader)) {
			$rawReqHeader = array();
			foreach ($reqheader as $key => $value){
				$rawReqHeader[] = "$key: $value";
			}
			curl_setopt($ch, CURLOPT_HTTPHEADER, $rawReqHeader);
			$this->_reqheader = array();
		}
		return $this;
	}
	
	public function downloadFile($file, $url) {
		$ch = curl_init();
		$fp = fopen($file, "w");
		if ($fp) {
			curl_setopt($ch, CURLOPT_URL, $url);
			$options = array(
					CURLOPT_RETURNTRANSFER => true,         // return web page
					CURLOPT_HEADER         => false,
					CURLOPT_FOLLOWLOCATION => true,         // follow redirects
					CURLOPT_ENCODING       => "",           // handle all encodings
					CURLOPT_USERAGENT      => "",     // who am i
					CURLOPT_AUTOREFERER    => true,         // set referer on redirect
					CURLOPT_CONNECTTIMEOUT => 10,          // timeout on connect
					CURLOPT_TIMEOUT        => 10,          // timeout on response
					CURLOPT_MAXREDIRS      => 10,           // stop after 10 redirects
					CURLOPT_POST            => false,            // i am sending post data
					CURLOPT_SSL_VERIFYHOST => 0,            // don't verify ssl
					CURLOPT_SSL_VERIFYPEER => false,        //
					// 						CURLOPT_FILE => $fp, //目标文件保存路径
					// 						CURLOPT_RETURNTRANSFER => 1
			);
			curl_setopt_array($ch, $options);
			$content = curl_exec($ch);
			$info = (curl_getinfo($ch));
			curl_close($ch);
			fwrite($fp, $content);
			fclose($fp);
			$result = array();
			if ($content && file_exists($file) && filesize($file)>0 && $info["content_type"]!="text/html" && $info["content_type"]!="text/plain") {
				return true;
			}
		}

		return false;

	}
}

?>